#include "common.h"

envelope2D::envelope2D()
	: minx_(std::numeric_limits<double>::max()), miny_(std::numeric_limits<double>::max()), maxx_(-1), maxy_(-1)
{

}

envelope2D::envelope2D(double minx, double miny, double maxx, double maxy)
	: minx_(minx), miny_(miny), maxx_(maxx), maxy_(maxy)
{
}

bool envelope2D::IsInRect(double x, double y) const
{
	if (x > minx_ && x < maxx_ && y > miny_ && y < maxy_)
	{
		return true;
	}
	return false;
}

Eigen::Vector2d envelope2D::GetCenter() const
{
	return Eigen::Vector2d((minx_ + maxx_) / 2.0, (miny_ + maxy_) / 2.0);
}

bool envelope2D::GetRange(Eigen::Vector2d &min, Eigen::Vector2d &max) const
{
	min.x() = minx_;
	min.y() = miny_;
	max.x() = maxx_;
	max.y() = maxy_;
	return true;
}

bool envelope2D::ExpandBy(double x, double y)
{
	if (x < minx_)
	{
		minx_ = x;
	}
	if (x > maxx_)
	{
		maxx_ = x;
	}
	if (y < miny_)
	{
		miny_ = y;
	}
	if (y > maxy_)
	{
		maxy_ = y;
	}
	return true;
}

bool envelope2D::ExpandBy(double radius)
{
	minx_ -= radius;
	maxx_ += radius;
	miny_ -= radius;
	maxy_ += radius;
	return true;
}

envelope2D envelope2D::GetEnvelope(const std::vector<Eigen::Vector3f> &geometry)
{
	double minx = std::numeric_limits<double>::max(), maxx = -std::numeric_limits<double>::max();
	double miny = std::numeric_limits<double>::max(), maxy = -std::numeric_limits<double>::max();
	for (std::size_t i = 0; i < geometry.size(); i++)
	{
		Eigen::Vector3f point3d = geometry[i];
		if (point3d.x() < minx)
		{
			minx = point3d.x();
		}
		if (point3d.x() > maxx)
		{
			maxx = point3d.x();
		}
		if (point3d.y() < miny)
		{
			miny = point3d.y();
		}
		if (point3d.y() > maxy)
		{
			maxy = point3d.y();
		}
	}
	return envelope2D(minx, miny, maxx, maxy);
}

envelope2D envelope2D::GetEnvelope(const std::vector<Eigen::Vector3d> &geometry)
{
	double minx = std::numeric_limits<double>::max(), maxx = -std::numeric_limits<double>::max();
	double miny = std::numeric_limits<double>::max(), maxy = -std::numeric_limits<double>::max();
	for (std::size_t i = 0; i < geometry.size(); i++)
	{
		Eigen::Vector3d point3d = geometry[i];
		if (point3d.x() < minx)
		{
			minx = point3d.x();
		}
		if (point3d.x() > maxx)
		{
			maxx = point3d.x();
		}
		if (point3d.y() < miny)
		{
			miny = point3d.y();
		}
		if (point3d.y() > maxy)
		{
			maxy = point3d.y();
		}
	}
	return envelope2D(minx, miny, maxx, maxy);
}

envelope2D envelope2D::GetEnvelope(const std::vector<Eigen::Vector2d> &geometry)
{
	double minx = std::numeric_limits<double>::max(), maxx = -std::numeric_limits<double>::max();
	double miny = std::numeric_limits<double>::max(), maxy = -std::numeric_limits<double>::max();
	for (std::size_t i = 0; i < geometry.size(); i++)
	{
		Eigen::Vector2d point2d = geometry[i];
		if (point2d.x() < minx)
		{
			minx = point2d.x();
		}
		if (point2d.x() > maxx)
		{
			maxx = point2d.x();
		}
		if (point2d.y() < miny)
		{
			miny = point2d.y();
		}
		if (point2d.y() > maxy)
		{
			maxy = point2d.y();
		}
	}
	return envelope2D(minx, miny, maxx, maxy);
}

double envelope2D::GetArea() const
{
	double x_range = maxx_ - minx_;
	double y_range = maxy_ - miny_;
	return x_range * y_range;
}

envelope2D envelope2D::Intersection(const envelope2D &r1, const envelope2D &r2)
{
	int minx = r1.minx_ > r2.minx_ ? r1.minx_ : r2.minx_;
	int miny = r1.miny_ > r2.miny_ ? r1.miny_ : r2.miny_;

	int maxx = r1.maxx_ < r2.maxx_ ? r1.maxx_ : r2.maxx_;
	int maxy = r1.maxy_ < r2.maxy_ ? r1.maxy_ : r2.maxy_;

	if (minx > maxx || miny > maxy)
	{
		return envelope2D(0, 0, 0, 0);
	}
	return envelope2D(minx, miny, maxx, maxy);
}

envelope2D envelope2D::Union(const envelope2D &r1, const envelope2D &r2)
{
	double minx = r1.minx_ < r2.minx_ ? r1.minx_ : r2.minx_;
	double miny = r1.miny_ < r2.miny_ ? r1.miny_ : r2.miny_;

	double maxx = r1.maxx_ > r2.maxx_ ? r1.maxx_ : r2.maxx_;
	double maxy = r1.maxy_ > r2.maxy_ ? r1.maxy_ : r2.maxy_;
	return envelope2D(minx, miny, maxx, maxy);
}

double envelope2D::IOU(const envelope2D &r1, const envelope2D &r2)
{
	double area_i = envelope2D::Intersection(r1, r2).GetArea();
	double area_u = envelope2D::Union(r1, r2).GetArea();
	if(area_u != 0)
	{
		return area_i / area_u;
	}
	return 0.0;
}

double envelope2D::Width() const
{
	return std::fabs(maxx_ - minx_);
}

double envelope2D::Height() const
{
	return std::fabs(maxy_ - miny_);
}

double envelope2D::GetMinx() const
{
	return minx_;
}

double envelope2D::GetMiny() const
{
	return miny_;
}

double envelope2D::CenterDiff(const envelope2D& r1, const envelope2D& r2)
{
	auto lhs_center = r1.GetCenter();
    auto rhs_center = r2.GetCenter();
    return (lhs_center - rhs_center).norm();
}
